/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "named_stub.h"
#include "gtest/gtest.h"
#include "mockcpp/mockcpp.hpp"
#include <boost/uuid/uuid.hpp>            // uuid class
#include <boost/uuid/uuid_generators.hpp> // generators
#include <boost/uuid/uuid_io.hpp>         // streaming operators etc.
#include <boost/algorithm/string/replace.hpp>
#include "constant/PluginConstants.h"
#include "system/System.hpp"
#include "common/CTime.h"
#include "io_device/FileSystemIO.hpp"
#include "common/Path.h"
#include "PluginUtilities.h"
#include <fstream>
#include "security/cmd/CmdExecutor.h"

using namespace std;

namespace {
    const uint64_t NUM_1024 = 1024;
    const uint64_t KB_size = NUM_1024;
    const uint64_t MB_size = NUM_1024 * KB_size;
    const uint64_t GB_size = NUM_1024 * MB_size;
    const uint64_t TB_size = NUM_1024 * GB_size;
    const uint64_t PB_size = NUM_1024 * TB_size;
}

class PluginUtilitiesTest : public testing::Test {
public:
    void SetUp();
    void TearDown();
    static void SetUpTestCase();
    static void TearDownTestCase();
    LLTSTUB::Stub stub;
};

static void returnVoidStub(void* obj)
{
    return;
}

void PluginUtilitiesTest::SetUp()
{
    stub.set(sleep, returnVoidStub);
}

void PluginUtilitiesTest::TearDown()
{
    GlobalMockObject::verify(); // 校验mock规范并清除mock规范
}
void PluginUtilitiesTest::SetUpTestCase() {}
void PluginUtilitiesTest::TearDownTestCase() {}

static void FunctionVoidSucc(void* obj) 
{
    return;
}

static bool FunctionBoolSucc(void* obj) 
{
    return true;
}

static bool FunctionBoolFailed(void* obj) 
{
    return false;
}

static int FunctionIntSucc(void* obj) 
{
    return Module::SUCCESS;
}

static int  FunctionIntFailed(void* obj) 
{
    return Module::FAILED;
}


static int runShellCmdWithOutput_failed(const severity_level& severity, const string& moduleName,
        const size_t& requestID, const string& cmd, const vector<string> params,
        vector<string>& cmdoutput, vector<string>& stderroutput)
{
    cmdoutput.push_back("nan jing");
    stderroutput.push_back("error...");
    return Module::FAILED;
}

static int runShellCmdWithOutput_success(const severity_level& severity, const string& moduleName,
        const size_t& requestID, const string& cmd, const vector<string> params,
        vector<string>& cmdoutput, vector<string>& stderroutput)
{
    cmdoutput.push_back("nan jing");
    stderroutput.push_back("error...");
    return Module::SUCCESS;
}

static int RunCommand_success()
{
    return Module::SUCCESS;
}

static bool CreateDirectory_Stub_TRUE(void* obj, const char* path)
{
    Module::FileSystemIO* o= (Module::FileSystemIO*)obj;
    return true;
}

static bool RemoveAll_Stub_Fail(void* obj, const char* path)
{
    Module::FileSystemIO* o= (Module::FileSystemIO*)obj;
    return false;
}

static bool CreateDirectory_Stub_FALSE(void* obj, const char* path)
{
    Module::FileSystemIO* o= (Module::FileSystemIO*)obj;
    return false;
}

 static boost::uintmax_t remove_all_success(string path) 
 {
     return true;
 }

 static bool remove_all_failed(string path) 
 {
     return false;
 }

/*
 * 用例名称:CheckDeviceNetworkConnect
 * 前置条件：
 * check点：输入是ip列表，以逗号分割，检查通过管理网络访问8088端口是否连通
 */
//  TEST_F(PluginUtilitiesTest, CheckDeviceNetworkConnect)
//  {
//      string str = "xxx";
//      bool ret = PluginUtils::CheckDeviceNetworkConnect(str);
//      EXPECT_EQ(ret, false);
// }

// TEST_F(PluginUtilitiesTest, CheckDeviceNetworkConnect_2) 
// {
//     string str = "xxx";
//     bool ret = PluginUtils::CheckDeviceNetworkConnect(str);
//     EXPECT_EQ(ret, false);
// }

/*
 * 用例名称:FormatTimeToStr
 * 前置条件：
 * check点：时间转字符串，转换结果形式为%Y-%m-%d-%H:%M:%S
 */
TEST_F(PluginUtilitiesTest, FormatTimeToStr) 
{
      
    time_t timeInSeconds = 1577257503;
    string res = "2019-12-25-15:05:03";

    string ret = PluginUtils::FormatTimeToStr(timeInSeconds);

    EXPECT_EQ(ret, res);
}

/*
 * 用例名称:KinitTGTInner
 * 前置条件：
 * check点：krb5配置
 */
TEST_F(PluginUtilitiesTest, KinitTGTInner) 
{
      
    stub.set(Module::runShellCmdWithOutput, runShellCmdWithOutput_success);

    string userName = "huawei";
    string password = "123456";
    string krb5Keytab = "xxx";
    string krb5Conf = "xxx";
    string uniqueFileJoinField = "xxx";

    int ret = PluginUtils::KinitTGTInner(userName, password, krb5Keytab, krb5Conf, uniqueFileJoinField);
    EXPECT_EQ(ret, Module::SUCCESS);

    // password为空，走另一逻辑
    password = "";
    ret = PluginUtils::KinitTGTInner(userName, password, krb5Keytab, krb5Conf, uniqueFileJoinField);
    EXPECT_EQ(ret, Module::SUCCESS);
    stub.reset(Module::runShellCmdWithOutput);
}

TEST_F(PluginUtilitiesTest, KinitTGTInner_2) 
{
    stub.set(Module::runShellCmdWithOutput, runShellCmdWithOutput_failed);

    string userName = "huawei";
    string password = "123456";
    string krb5Keytab = "xxx";
    string krb5Conf = "xxx";
    string uniqueFileJoinField = "xxx";

    int ret = PluginUtils::KinitTGTInner(userName, password, krb5Keytab,krb5Conf, uniqueFileJoinField);
    EXPECT_EQ(ret, 65535);

    stub.reset(Module::runShellCmdWithOutput);
}

/*
 * 用例名称:Base64Encode
 * 前置条件：
 * check点：检查编码解码功能
 */
TEST_F(PluginUtilitiesTest, Base64Encode) 
{
    string str = "aaa";
    string ret = PluginUtils::Base64Encode(str);
      
    EXPECT_EQ(PluginUtils::Base64Decode(ret), str);
}

/*
 * 用例名称:GenerateHash
 * 前置条件：
 * check点：获得hash值
 */
TEST_F(PluginUtilitiesTest, GenerateHash) 
{
    size_t ret = PluginUtils::GenerateHash("55");
    EXPECT_EQ(ret, ret);
}

/*
 * 用例名称:GetCurrentTimeInSeconds
 * 前置条件：
 * check点：获得当前时间点距离1970.01.01 00：00：00的秒数量
 */
TEST_F(PluginUtilitiesTest, GetCurrentTimeInSeconds) 
{
    string dateAndTimeString;
    time_t ret = PluginUtils::GetCurrentTimeInSeconds(dateAndTimeString);

    time_t currTime;
    Module::CTime::Now(currTime);

    EXPECT_EQ(ret, currTime);
}

/*
 * 用例名称:CreateDirectory
 * 前置条件：
 * check点：创建目录，可多层
 */
TEST_F(PluginUtilitiesTest, CreateDirectory_TURE) 
{
    string path = ""; // path is empty
    bool ret = PluginUtils::CreateDirectory(path);
    EXPECT_EQ(ret, true);

    typedef bool (*fptr)(Module::FileSystemIO*, const char*);
    fptr FileSystemIO_CreateDirectory = (fptr)(&Module::FileSystemIO::CreateDirectory);
    stub.set(FileSystemIO_CreateDirectory, CreateDirectory_Stub_TRUE);
    path = "/xxx";
    ret = PluginUtils::CreateDirectory(path);
    EXPECT_EQ(ret, true);
    stub.reset(ADDR(Module::FileSystemIO, CreateDirectory));
}

/*
 * 用例名称:IsPathExists
 * 前置条件：
 * check点：文件是否存在
 */
TEST_F(PluginUtilitiesTest, IsPathExists_FALSE)
{
    string path = "/pathNotExists";
    EXPECT_FALSE(PluginUtils::IsPathExists(path));
}

/*
 * 用例名称:IsPathExists
 * 前置条件：
 * check点：文件是否存在
 */
TEST_F(PluginUtilitiesTest, IsPathExists_TRUE)
{
    string path = "/";
    EXPECT_TRUE(PluginUtils::IsPathExists(path));
}

/*
 * 用例名称:IsPathExists
 * 前置条件：
 * check点：文件是否存在
 */
TEST_F(PluginUtilitiesTest, IsPathExists)
{
    string str = "err";
    boost::filesystem::path path1 = "/a/b.txt";
    boost::filesystem::path path2 = "/a/b.txt";
    boost::system::error_code ec;
    MOCKER_CPP(boost::filesystem::exists, bool(const boost::filesystem::path&))
            .stubs()
            .will(throws(boost::filesystem::filesystem_error(str, path1, path2, ec)));
    string path = "/";
    EXPECT_FALSE(PluginUtils::IsPathExists(path));
}

/*
 * 用例名称:Rename
 * 前置条件：
 * check点：文件重命名或实现文件移动
 */
TEST_F(PluginUtilitiesTest, Rename) 
{
    // string srcPath = "/";
    // string dstPath = "/xxy";
    // bool ret = PluginUtils::Rename(srcPath, dstPath);

    // EXPECT_EQ(ret, true);
}

/*
 * 用例名称:Rename2
 * 前置条件：
 * check点：文件重命名或实现文件移动
 */
TEST_F(PluginUtilitiesTest, Rename2)
{
    string str1 = "/a";
    string str2 = "/b";
    EXPECT_FALSE(PluginUtils::Rename(str1, str2));
}

/*
 * 用例名称:Remove2
 * 前置条件：
 * check点：删除目录
 */
TEST_F(PluginUtilitiesTest, Remove2)
{
    string path = "/dddd";
    EXPECT_TRUE(PluginUtils::Remove(path));
}

/*
 * 用例名称:Remove_Fail
 * 前置条件：
 * check点：删除目录
 */
TEST_F(PluginUtilitiesTest, Remove_Fail)
{
    // string path = "/";
    // bool ret = PluginUtils::Remove(path);
    // EXPECT_EQ(ret, false);
}

/*
 * 用例名称:RemoveDirectory_Fail
 * 前置条件：
 * check点：删除目录
 */
TEST_F(PluginUtilitiesTest, RemoveDirectory_Fail)
{
    typedef bool (*fptr)(Module::FileSystemIO*, const char*);
    fptr FileSystemIO_RemoveDirectory = (fptr)(&Module::FileSystemIO::RemoveAll);
    stub.set(FileSystemIO_RemoveDirectory, RemoveAll_Stub_Fail);
    string path = "/sdg";
    bool ret = PluginUtils::RemoveDirectory(path);
    EXPECT_EQ(ret, false);
    stub.reset(ADDR(Module::FileSystemIO, RemoveAll));
}

/*
 * 用例名称:FormatCapacity
 * 前置条件：
 * check点：格式化存储容量
 */

 TEST_F(PluginUtilitiesTest, FormatCapacity)
{
    uint64_t capacity = 512;
    string ret = PluginUtils::FormatCapacity(capacity);
    EXPECT_EQ(ret, "512B");

    capacity = KB_size *2;
    ret = PluginUtils::FormatCapacity(capacity);
    EXPECT_EQ(ret, "2.0KB");

    capacity = MB_size *2;
    ret = PluginUtils::FormatCapacity(capacity);
    EXPECT_EQ(ret, "2.0MB");

    capacity = GB_size *2;
    ret = PluginUtils::FormatCapacity(capacity);
    EXPECT_EQ(ret, "2.0GB");

    capacity = TB_size *2;
    ret = PluginUtils::FormatCapacity(capacity);
    EXPECT_EQ(ret, "2.00TB");

    capacity = PB_size *2;
    ret = PluginUtils::FormatCapacity(capacity);
    EXPECT_EQ(ret, "2.0PB");
}

/*
 * 用例名称:GetFileName
 * 前置条件：
 * check点：根据文件路径获取文件名字
 */
TEST_F(PluginUtilitiesTest, GetFileName) 
{
    string filePath = "/xxx/yyy/file.cpp";
    string fileName = "file.cpp";
    string ret = PluginUtils::GetFileName(filePath);

    EXPECT_EQ(ret, fileName);
}

/*
 * 用例名称:RemoveFile
 * 前置条件：
 * check点：删除文件
 */
TEST_F(PluginUtilitiesTest, RemoveFile) 
{
    string filePath = "/xxx/yyy/file.cpp";
    bool ret = PluginUtils::RemoveFile(filePath);

    EXPECT_EQ(ret, true);
}

static bool CopySuccess_Stub (void* obj)
{
    return true;
}

/*
 * 用例名称:CopyFile
 * 前置条件：
 * check点：拷贝文件
 */
 TEST_F(PluginUtilitiesTest, CopyFile_Fail)
{
    string srcfile = "/";
    string dstfile = "/xxx/yyy/file.cpp";
    bool ret = PluginUtils::CopyFile(srcfile, dstfile);  
    EXPECT_EQ(ret, false);
}

/*
 * 用例名称:CopyFile_Success
 * 前置条件：
 * check点：拷贝文件
 */
TEST_F(PluginUtilitiesTest, CopyFile_Success)
{
    string srcfile = "/";
    string dstfile = "/xxx/yyy/file.cpp";
    typedef bool (*fptr)(Module::FileSystemIO*, const char*);
    fptr FileSystemIO_Copy = (fptr)(&Module::FileSystemIO::Copy);
    stub.set(FileSystemIO_Copy, CopySuccess_Stub);
    bool ret = PluginUtils::CopyFile(srcfile, dstfile);
    EXPECT_EQ(ret, true);
    stub.reset(ADDR(Module::FileSystemIO, Copy));
}

/*
 * 用例名称:GetFileListInDirectory
 * 前置条件：
 * check点：获得某路径下文件列表
 */
TEST_F(PluginUtilitiesTest, GetFileListInDirectory) 
{
    string dir = "/xxx/yyy";
    vector<string> fileList;
    bool ret = PluginUtils::GetFileListInDirectory(dir, fileList);
    
    EXPECT_EQ(ret, false);
}

TEST_F(PluginUtilitiesTest, GetFileListInDirectory_2) 
{
    string dir = "/";
    vector<string> fileList;
    bool ret = PluginUtils::GetFileListInDirectory(dir, fileList);
    
    EXPECT_EQ(ret, true);
}

/*
 * 用例名称:测试递归生成目录
 * 前置条件：多级目录
 * check点：生成目录成功，跳过已存在
 */
TEST_F(PluginUtilitiesTest, RecurseCreateDirectory) {
    std::string path = "/tmp/llt/RecurseCreateDirectoryTest";
    bool ret = PluginUtils::RecurseCreateDirectory(path);
    EXPECT_EQ(ret, true);
}

/*
 * 用例名称:测试递归生成目录
 * 前置条件：多级目录
 * check点：生成目录成功，跳过已存在
 */
TEST_F(PluginUtilitiesTest, FormatSpeed) {
    uint64_t speed = MB_size * 2;
    std:: string ret = PluginUtils::FormatSpeed(speed);
    EXPECT_EQ(ret, "2.0MBps");

    speed = GB_size * 2;
    ret = PluginUtils::FormatSpeed(speed);
    EXPECT_EQ(ret, "2.0GBps");

    speed = TB_size * 2;
    ret = PluginUtils::FormatSpeed(speed);
    EXPECT_EQ(ret, "2.0TBps");

    speed = KB_size * 2;
    ret = PluginUtils::FormatSpeed(speed);
    EXPECT_EQ(ret, "2.0KBps");

    speed = PB_size * 2;
    ret = PluginUtils::FormatSpeed(speed);
    EXPECT_EQ(ret, "2.0PBps");

    speed = 1;
    ret = PluginUtils::FormatSpeed(speed);
    EXPECT_EQ(ret, "1Bytes/s");
}

/*
 * 用例名称:MountNFS
 * 前置条件：无
 * check点：MountNas成功或失败
 */
// TEST_F(PluginUtilitiesTest, MountNFS) {
//     struct PluginUtils::NasMountParams param = {"TCP", "1.0", "/opt", "/css", "Normal", "root", "pwd", {"12.12.12.12"}};
//     std::string outSelectedSvcIp = "";
//     int ret = PluginUtils::MountNFS(param, outSelectedSvcIp);
//     EXPECT_EQ(ret, 65535);

//     // stub.set(Module::RunCommand, RunCommand_success);
//     ret = PluginUtils::MountNFS(param, outSelectedSvcIp);
//     EXPECT_NE(ret, 0);
//     // stub.reset(Module::RunCommand);
// }

/*
 * 用例名称:UnmountNas
 * 前置条件：无
 * check点：UnmountNas成功或失败
 */
// TEST_F(PluginUtilitiesTest, UnmountNas) {
//     std::string localPath = "";
//     int ret = PluginUtils::UnmountNas(localPath);
//     EXPECT_NE(ret, 0);

//     // stub.set(Module::RunCommand, RunCommand_success);
//     ret = PluginUtils::UnmountNas(localPath);
//     EXPECT_NE(ret, 0);
//     // stub.reset(Module::RunCommand);
// }

/*
 * 用例名称:UnmountNas
 * 前置条件：无
 * check点：UnmountNas成功或失败
 */
TEST_F(PluginUtilitiesTest, testStandardPath) {
    std::string localPath = "///c///d";
    std::string standardPath = PluginUtils::StandardPath(localPath);
    EXPECT_EQ(standardPath, "/c/d");
}